'''
Created on 2010/07/06

@author: yukiyonezawa
'''
from pyrfid.DataProtocol.DSFID.AccessMethod.AccessMethod import AccessMethod
from pyrfid.DataProtocol.DSFID.DataFormat.DataFormatUtil import DataFormatUtil
from xml.dom import minidom
from pyrfid.DataProtocol.DSFID.AccessMethod.PackedObjects.IDTable import IDTable
from pyrfid.DataProtocol.DSFID.AccessMethod.PackedObjects.IDInfo import IDInfo

from pyrfid.asn1.Encoder import Encoder
import sys
from pyrfid.DataProtocol.DSFID.AccessMethod.PackedObjects.ObjectInfo import ObjectInfo
from pyrfid.DataProtocol.DSFID.AccessMethod.PackedObjects.SecondaryID import SecondaryID
from pyrfid.DataProtocol.DSFID.AccessMethod.PackedObjects.AuxFormat import AuxFormat
from pyrfid.DataProtocol.DSFID.AccessMethod.PackedObjects.Data import Data
from pyrfid.DataProtocol.DSFID.AccessMethod.PackedObjects.FormatFlag import FormatFlag
from pyrfid.Util.Util import Util
from pyrfid.DataProtocol.AddObject import AddObject


class PackedObjects(AccessMethod):
    '''
    classdocs
    '''
    ACCESS_METHOD_ID = 2;
    driver = object()
    
    
    #the structure of packed object 
    OPTIONAL_FORMAT_FLAG = ""
    OBJECT_INFO = ""
    ALLOW_COMPACTION_TYPE = [AddObject.APPLICATION_DEFINED, AddObject.COMPACT, AddObject.UTF8_DATA, AddObject.PACKE_OBJECT]
    
    
    __id_table = object()
    
    def __init__(self):
        '''
        Constructor
        '''
        pass
    
    def setDriver(self, driver):
        self.driver = driver
    
    def readAndAnalyzeLogicalMemoryMap(self, tag_id, start_address, data_format, defined_packed_objects):
        '''
        start_address -- a int data
        data_format -- Data Format
        ''' 
        
        index = start_address 
        #1.format flag
        format_flag_hex = self.driver.readUserMemory("tagId", index, 1)
        if format_flag_hex =="00":
            return []
                 
        format_flag = FormatFlag.analyzeFormatFlag(format_flag_hex)
        
        #Object Info
        index += 1
        object_info, all_data_bit = ObjectInfo.analyzeObjectInfo(tag_id, self.driver, index, format_flag.getBaseStructureType())        
        
        included_idInfo = list()
        if object_info != None:            
            for id in object_info.getIDListing():               
                for idInfo in defined_packed_objects.getIDInfoAll():                    
                    if idInfo.getIDValue() == str(int(id, 2)):
                        included_idInfo.append(idInfo)
                        break
        else:
            raise "UNIMPLEMENTED. for IDMLO"
        
        #check have secondary_id
        
        #Secondary Id
        secondary_id, all_data_bit = SecondaryID.analyzeSecondaryId(all_data_bit, included_idInfo)
        
        #Aux Format
        auxformat, all_data_bit = AuxFormat.analyzeAuxFormat(all_data_bit, object_info, included_idInfo)
        
        #Data Section
        added_objects_list = Data.analyzeData(all_data_bit, secondary_id, auxformat , included_idInfo, defined_packed_objects)
        
        return added_objects_list
    
    def addData(self, tag_id, dsfid_on_tag, data_format, add_objects_list):       
        #read profile header
        print "PackedObject"
        
        #@todo: implements compaction type
        for add_object in add_objects_list:
            if str(add_object.COMPACT_PARAMETER) != "3":
                raise "UNIMPLEMENTED COMPACTION TYPE"
        
        #1.load a profile table file
        defined_packed_objects = self.__loadPackedObjectsTable("PO-F99B0.xml")
        
        print dsfid_on_tag
        
        if dsfid_on_tag != "":
            added_objects_list = self.readAndAnalyzeLogicalMemoryMap(tag_id, 0, data_format, defined_packed_objects)
            #merge added_objects_list to add_objects_list    
            add_objects_list += added_objects_list
            
        ########################################################################
        # convert add_object_list to DataSet
        # print added_objects_list
        # print add_objects_list
        add_data_set_list = DataFormatUtil.convertDataSet(add_objects_list)        
        
        #2.change the order       
        changed_order_add_data_set_list = self.__changeOrder(defined_packed_objects, add_data_set_list)

        #build Identifiers
        object_info = ObjectInfo(FormatFlag.BASE_IDLPO)
        object_info.build(defined_packed_objects, changed_order_add_data_set_list)
        
        secondary_id = SecondaryID()
        secondary_id.build(defined_packed_objects, changed_order_add_data_set_list)
        
        #this parameter received by add object parameter.
        application_compaction_type = add_objects_list[0].COMPACT_PARAMETER
        
        for add_object in add_objects_list:
            if str(application_compaction_type) != str(add_object.COMPACT_PARAMETER):                
                raise "SOME COMPACTION TYPE REQUIRED!!"
        
        auxformat = AuxFormat(application_compaction_type)
        auxformat.build(defined_packed_objects, changed_order_add_data_set_list)        
        
        #build Compacted Object 
        data_session = Data()
        data_session.build(defined_packed_objects, changed_order_add_data_set_list)
        
        #build Metadata
        format_flag = FormatFlag(object_info.getBinary()["STRUCTURE_NAME"], FormatFlag.N_IDLPO_DEFAULT, FormatFlag.A_IDLPO_NON_ADDENDUM)
        
        connected_all_section_bit = ""
        object_info.setLengthInformation(secondary_id, auxformat, data_session)
#        
#        print "check code ++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
#        if format_flag.getBit() != "00001000":
#            raise "ERROR: FORMAT_FLAG"
#                
#        if secondary_id.getBit() != "0010":
#            raise "ERROR: SECONDARY_ID"
#        
#        print auxformat.getBit()
#        if auxformat.getBit() != "1101100000110":
#            raise "ERROR: AUXFORMAT"
#        
#        print data_session.getBit()
#        if data_session.getBit() != "0000111011100110011111101001001100111110101100000000001001000110001111000100100000000000111000011011110":
#            raise "ERROR: Data Session"     
#        
#        print object_info.getBit()
#        if object_info.getBit() != "010010000111111010110011":
#            raise "ERROR: OBJECT_INFO"
#        print "++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
##        
        #connect all section!
        #1.Format Flag
        connected_all_section_bit += format_flag.getBit()                
        #2.Object Info
        connected_all_section_bit += object_info.getBit()
        print "object_info", object_info.getBit()
        #3.Secondary Id
        connected_all_section_bit += secondary_id.getBit()        
        print "secondary :", secondary_id.getBit()
        #4.Aux Format
        connected_all_section_bit += auxformat.getBit()
        print "aux form bit :", auxformat.getBit()              
        #5.Data Section
        connected_all_section_bit += data_session.getBit()        
        print "data bit :", data_session.getBit()
        
        self.driver.writeUserMemory(tag_id, 0, Util.convertBitToHex(connected_all_section_bit))
    
    def readData(self, tag_id, data_format, read_type):
                
        #1.load a profile table file
        defined_packed_objects = self.__loadPackedObjectsTable("PO-F99B0.xml")        
        added_objects_list = self.readAndAnalyzeLogicalMemoryMap(tag_id, 0, data_format, defined_packed_objects)
        return added_objects_list
    
    def __loadPackedObjectsTable(self, file_name):
        
        filePath = "./"+file_name
        
        idTable = IDTable()
        
        tree = minidom.parse(filePath)
        
        kText = tree.getElementsByTagName('K-Text')
        if kText:
            idTable.setKText(kText[0].firstChild.data)
    
        kVersion = tree.getElementsByTagName('K-Version')
        if kVersion:
            idTable.setKVersion(kVersion[0].firstChild.data)
    
        kTableID = tree.getElementsByTagName('K-TableID')
        if kTableID:
            idTable.setKTableID(kTableID[0].firstChild.data)
    
        kRootOID = tree.getElementsByTagName('K-RootOID')
        if kRootOID:
            idTable.setKRootOID(kRootOID[0].firstChild.data)
    
        kIDSize = tree.getElementsByTagName('K-IDSize')
        if kIDSize:
            idTable.setKIDSize(kIDSize[0].firstChild.data)
    
        kTableEnd = tree.getElementsByTagName('K-TableEnd')
        if kTableEnd:
            idTable.setKTableEnd(kTableEnd[0].firstChild.data)
    
        idTree = tree.getElementsByTagName('OIDList')
        if idTree:
            infos = []
            for id in idTree[0].childNodes:
                if id.childNodes.length != 0:
                    idInfo = IDInfo()
                    idValue = id.getElementsByTagName('IDValue')
                    idInfo.setIDValue(idValue[0].firstChild.data)

                    idString = id.getElementsByTagName('OIDs')
                    try:
                        idInfo.setOIDString(idString[0].firstChild.data)
                    except:
                        idInfo.setOIDString("")
                                                
                    formatStr = id.getElementsByTagName('FormatString')
                    try:
                        idInfo.setFormatStr(formatStr[0].firstChild.data)
                    except:
                        idInfo.setFormatStr("")
                        
                    infos.append(idInfo)
            idTable.setIDInfo(infos)
        
        return idTable

    
    def __changeOrder(self, defined_packed_objects, add_data_set_list):        
        
        rootOID =  defined_packed_objects.getKRootOID().replace(".", " ")
        
        #combination list
        combination_list = []
                
        #single list
        temp_single_idInfo_list = []
        
        idInfo_list = defined_packed_objects.getIDInfo(True)        
        idInfo_list.reverse()
        
        #check combination        
        for idInfo in idInfo_list:
            included_count = 0
            temp_combination_list = []            
            for oid in idInfo["ID"]:
                encoded_oid = Encoder.oidEncode(rootOID + " "+ oid)                
                for data_set in add_data_set_list:
                    if data_set.OBJECT_IDENTIFIER == encoded_oid:
                        temp_combination_list.append(data_set)
                        included_count += 1
            
            if included_count == len(idInfo["ID"]):
                combination_list += temp_combination_list
        
        for data_set in add_data_set_list:
            is_remove = False    
            for remove_data_set in combination_list:
                if remove_data_set.OBJECT_IDENTIFIER == data_set.OBJECT_IDENTIFIER:
                    is_remove = True
            if is_remove == False:
                temp_single_idInfo_list.append(data_set)
        
        
        single_idInfo_list = []
         
        idInfo_list = defined_packed_objects.getIDInfo()        
        idInfo_list.reverse()
        for idInfo in idInfo_list:
            for oid in idInfo.getOIDString():
                encoded_oid = Encoder.oidEncode(rootOID + " "+ oid)                
                for data_set in temp_single_idInfo_list:
                    if encoded_oid == data_set.OBJECT_IDENTIFIER:
                        single_idInfo_list.append(data_set)
        
        return combination_list + single_idInfo_list
    
    def __buildIDLPO(self, changed_order_add_data_set_list):
        pass
    
    def __buildIDMPO(self, changed_order_add_data_set_list):
        pass
    

        
    
if __name__ == '__main__':
    po = PackedObjects()
    idTable = po.loadPackedObjectsTable("PO-F99B0.xml")
    
    

    
    
    
    
    